package org.rc.web;

import com.google.appengine.api.memcache.stdimpl.GCacheFactory;
import com.google.visualization.datasource.DataSourceServlet;
import com.google.visualization.datasource.base.TypeMismatchException;
import com.google.visualization.datasource.datatable.ColumnDescription;
import com.google.visualization.datasource.datatable.DataTable;
import com.google.visualization.datasource.datatable.value.ValueType;
import com.google.visualization.datasource.query.Query;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import javax.cache.Cache;
import javax.cache.CacheException;
import javax.cache.CacheFactory;
import javax.cache.CacheManager;
import javax.servlet.http.HttpServletRequest;
import org.rc.dao.GenericDao;
import org.rc.dao.ResultDao;
import org.rc.model.Race;
import org.rc.model.Result;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Servlet to handle requests for the google visualisation pie chart
 * @author dokeeffe
 */
public class RaceVisServlet extends DataSourceServlet {

    private static final Logger LOGGER = LoggerFactory.getLogger(RaceVisServlet.class);
    private Cache cache;
    private GenericDao<Race> raceDao = new GenericDao<Race>(Race.class);
    private ResultDao resultDao = new ResultDao();
    private static Map<Integer,String> pacegroups = new TreeMap<Integer,String>();

    static {
        pacegroups.put(270, "4:30/mile");
        pacegroups.put(300, "5:00/mile");
        pacegroups.put(330, "5:30/mile");
        pacegroups.put(360, "6:00/mile");
        pacegroups.put(390, "6:30/mile");
        pacegroups.put(420, "7:00/mile");
        pacegroups.put(450, "7:30/mile");
        pacegroups.put(480, "8:00/mile");
        pacegroups.put(510, "8:30/mile");
        pacegroups.put(540, "9:00/mile");
        pacegroups.put(570, "9:30/mile");
        pacegroups.put(600, "10:00/mile");
        pacegroups.put(630, "10:30/mile");
        pacegroups.put(660, "11:00/mile");
        pacegroups.put(690, "11:30/mile");
        pacegroups.put(720, "12:00/mile");
        pacegroups.put(750, "12:30/mile");
        pacegroups.put(780, "13:00/mile");
        pacegroups.put(810, "13:30/mile");
        pacegroups.put(840, "14:00/mile");
        pacegroups.put(870, "14:30/mile");
        pacegroups.put(99999, "Others");
    }
    
    /**
     * Constructor
     */
    public RaceVisServlet() {
        Map props = new HashMap();
        props.put(GCacheFactory.EXPIRATION_DELTA, 14400);//4hrs cache expiry
        try {
            CacheFactory cacheFactory = CacheManager.getInstance().getCacheFactory();
            cache = cacheFactory.createCache(props);
        } catch (CacheException e) {
            LOGGER.error("error setting up cache", e);
        }
    }

    /**
     * Allow x domain access
     * @return 
     */
    @Override
    protected boolean isRestrictedAccessMode() {
        return false;
    }

    /**
     * Request handler. Generates the data table for the pace breakdown pie chart
     * @param query
     * @param request
     * @return 
     */
    @Override
    public DataTable generateDataTable(Query query, HttpServletRequest request) {
        Long id = new Long(request.getParameter("id"));
        LOGGER.debug("Create a data table for race " + id);
        Race race = loadRace(id);
        List<Result> results = loadResults(id, race);
        DataTable dataTable = generatePacePieChartTable(results);
        return dataTable;
    }

    /**
     * Generate the datatable for the pie chart.
     * Will generate max 6 slices, bundling all slower slices into the last 6th slice.
     * 
     * @param results
     * @return 
     */
    protected DataTable generatePacePieChartTable(List<Result> results) {
        Integer[] groups = pacegroups.keySet().toArray(new Integer[0]);//{270,300,330,360,390,420,450,480,510,540,99999};
        Arrays.sort(groups);
        
        Map<Integer,Integer> dataTableBuckets = new HashMap<Integer,Integer>();
        
        List<Long> paces = new ArrayList<Long>();
        for(Result r : results) {
            paces.add(r.getSecondsPerMilePace());
        }
        Collections.sort(paces);
        int bucketCounter =0;
        for(long pace: paces) {
            if(pace < groups[bucketCounter]) {
                Integer bucketVal = dataTableBuckets.get(groups[bucketCounter]);
                if(bucketVal==null){
                    bucketVal = new Integer(0);
                }
                bucketVal++;
                dataTableBuckets.put(groups[bucketCounter], bucketVal);
            } else {
                //find next bucket up to put result
                boolean foundBucket=false;
                while(!foundBucket) {
                    bucketCounter++;
                    if(pace < groups[bucketCounter]) { //FIXME: http://card-results.appspot.com/race?id=775833
                        Integer bucketVal = dataTableBuckets.get(groups[bucketCounter]);
                        if(bucketVal==null){
                            bucketVal = new Integer(0);
                        }
                        bucketVal++;
                        dataTableBuckets.put(groups[bucketCounter], bucketVal);
                        foundBucket=true;
                    }
                }
                
            }
        }
        
        DataTable data = new DataTable();
        ArrayList cd = new ArrayList();
        cd.add(new ColumnDescription("pace-group", ValueType.TEXT, "Pace Group"));
        cd.add(new ColumnDescription("numbers", ValueType.NUMBER, "numbers"));
        data.addColumns(cd);
        // Fill the data table.
        Integer[] paceKeys =  dataTableBuckets.keySet().toArray(new Integer[0]);
        Arrays.sort(paceKeys);
        try {
            int pieSliceCount=0;
            String lastPieSliceName = null;
            int lastPieSliceValue=0;
            for(Integer paceKey: paceKeys) {
                pieSliceCount++;
                if(pieSliceCount<6) {
                    data.addRowFromValues("Sub " + pacegroups.get(paceKey),dataTableBuckets.get(paceKey));
                    lastPieSliceName=pacegroups.get(paceKey);
                } else {
                    //merge all remaining into one slice
                    lastPieSliceValue += dataTableBuckets.get(paceKey);
                }
            }
            if(lastPieSliceValue>0) {
                data.addRowFromValues("Slower than " + lastPieSliceName,lastPieSliceValue);
            }
        } catch (TypeMismatchException e) {
            LOGGER.error("error generating datatable", e);
        }
        return data;
    }

    /**
     * Load results from datastore
     * @param id
     * @param race
     * @return 
     */
    private List<Result> loadResults(Long id, Race race) {
        List<Result> results = resultDao.getAllResultsForRace(race);
        return results;
    }

    /**
     * Load a race from cache/datastore
     * @param id
     * @return 
     */
    private Race loadRace(Long id) {
        Race race = (Race) cache.get("race" + id);
        if (race == null) {
            race = raceDao.get(id);
            cache.put("race" + id, race);
        }
        return race;
    }
}